/**
 *
 */
package soa.security.jpa.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Resource;

import soa.security.jpa.domain.Change;
import soa.security.jpa.repository.ChangeRepository;
import soa.security.jpa.service.ChangeMgrService;
import soa.security.jpa.service.ChangePolicy;

/**
 * 定时，定量的保存变更，参考CPI日志保存策略
 *
 * @author Cre.Gu
 *
 */
public class DefaultChangePolicy implements ChangePolicy {
	protected final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(this.getClass());

	@Resource
	private ChangeRepository changeRepository;
	@Resource
	private ChangeMgrService changeMgrService;

	private int timeout = 10000; // 超时后，写入剩余日志
	private int batchNumber = 100; // 超数量后，写入剩余日志

	private Timer timer;

	/**
	 * 并发队列
	 */
	private ConcurrentLinkedQueue<Change> results = new ConcurrentLinkedQueue<Change>();
	private AtomicInteger count = new AtomicInteger(0);

	public DefaultChangePolicy(int timeout) {
		this.timeout = timeout;
		// 超时后，写入剩余日志
		timer = new Timer();
		// 定时监测队列数，写入未超量的日志内容
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				record(null);
			}

		}, 0, this.timeout);
	}

	@Override
	public void record(Change change) {
		if (change != null) {
			results.add(change);
			count.incrementAndGet();
		}

		// 当队列数量足够大，一次性批量计入数据库
		if (count.compareAndSet(batchNumber, 0) || change == null) {

			List<Change> writeLogs = new ArrayList<Change>();
			int i = 0;
			for (; i < batchNumber && !results.isEmpty(); i++) {
				Change item = results.poll();

				writeLogs.add(item);
			}

			if (writeLogs.size() > 0) {
				if (log.isTraceEnabled()) {
					log.trace("{}触发变更批量写入", (change == null ? "超时" : "超量"));
				}

				for (Change change2 : writeLogs) {
					changeMgrService.save(change2);
				}
			}
		}
	}

	public int getBatchNumber() {
		return batchNumber;
	}

	public void setBatchNumber(int batchNumber) {
		this.batchNumber = batchNumber;
	}
}
